#include "AST.h"
#include "ASTNode.h"
#include "ASTNodeType.h"
#include "ASTLinkType.h"
#include "ParentLink.h"
#include "SiblingLink.h"
#include "ChildLink.h"
#include <string>
#include <vector>

using namespace std;

AST::AST()
{
	//const unsigned int ROOT_INDEX = 0;

	this->rootNode = new ASTNode(ASTNodeType::PROGRAM,"root",0);
	this->nodes.push_back(rootNode);

	//initialize the links table
	//this->linkTable = new ASTLinksTable();
}

vector<ASTNode*> AST::addNode(ASTNode* node)
{
	this->nodes.push_back(node);
	return this->nodes;
}

/*void AST::insertLink(ASTLink* nodeLink)
{
	this->linkTable->insertLink(nodeLink);
}*/

void AST::setParentLink(ASTNode* fromNode, ASTNode* toNode)
{
	ParentLink* parentLink = new ParentLink(fromNode->getNodeIndex(), toNode->getNodeIndex());
	parentTable.push_back(parentLink);
}

void AST::setSiblingLink(ASTNode* fromNode, ASTNode* toNode)
{
	SiblingLink* siblingLink = new SiblingLink(fromNode->getNodeIndex(), toNode->getNodeIndex());
	siblingTable.push_back(siblingLink);
}

void AST::setChildLink(ASTNode* fromNode, ASTNode* toNode, unsigned int childNumber)
{
	ChildLink* childLink = new ChildLink(fromNode->getNodeIndex(), toNode->getNodeIndex(), childNumber);
	childTable.push_back(childLink);
}

/*void AST::printTable(void)
{
	this->linkTable->printTable();
}*/

//Not done yet
ASTNode* AST::getRootNode()
{
	return this->getNodeByIndex(ROOT_INDEX);
}

ASTNode* AST::getParentNode(ASTNode* fromNode)
{
	unsigned int fromNodeIndex = fromNode->getNodeIndex();
	for (unsigned int i=0; i<this->parentTable.size();++i)
	{
		ParentLink* tmpLink = this->parentTable.at(i);
		if(tmpLink->getFromNode()==fromNodeIndex)
		{
			return this->getNodeByIndex(tmpLink->getToNode());
		}
	}
	return nullptr;
}

ASTNode* AST::getChildNode(ASTNode* fromNode, unsigned int childNumber)
{
	unsigned int fromNodeIndex = fromNode->getNodeIndex();
	for (unsigned int i=0; i<this->childTable.size();++i)
	{
		ChildLink* tmpLink = this->childTable.at(i);
		if(tmpLink->getFromNode()==fromNodeIndex && tmpLink->getChildNumber()==childNumber)
		{
			return this->getNodeByIndex(tmpLink->getToNode());
		}
	}
	return nullptr;
}

ASTNode* AST::getSiblingNode(ASTNode* fromNode)
{
	unsigned int fromNodeIndex = fromNode->getNodeIndex();
	for (unsigned int i=0; i<this->siblingTable.size();++i)
	{
		SiblingLink* tmpLink = this->siblingTable.at(i);
		if(tmpLink->getFromNode()==fromNodeIndex)
		{
			return this->getNodeByIndex(tmpLink->getToNode());
		}
	}
	return nullptr;
}

vector<ASTNode*> AST::getRevParentNode(ASTNode* toNode)
{
	//return nullptr;
	unsigned int nodeIndex = toNode->getNodeIndex();
	vector<ASTNode*> result;
	for (unsigned int i=0; i<this->parentTable.size();++i)
	{
		ParentLink* tmpLink = this->parentTable.at(i);
		if(tmpLink->getToNode()==nodeIndex)
		{
			result.push_back( getNodeByIndex(tmpLink->getFromNode()) );
		}
	}
	// if there are no results, vector size will be 0
	return result;
}

ASTNode* AST::getRevSiblingNode(ASTNode* node)
{
	unsigned int nodeIndex = node->getNodeIndex();
	for (unsigned int i=0; i<this->siblingTable.size();++i)
	{
		SiblingLink* tmpLink = this->siblingTable.at(i);
		if(tmpLink->getToNode()==nodeIndex)
		{
			return getNodeByIndex(tmpLink->getFromNode());
		}
	}
	return nullptr;
}

ASTNode* AST::getRevChildNode(ASTNode* node)
{
	unsigned int nodeIndex = node->getNodeIndex();
	for (unsigned int i=0; i<this->childTable.size();++i)
	{
		ChildLink* tmpLink = this->childTable.at(i);
		if(tmpLink->getToNode()==nodeIndex)
		{
			return getNodeByIndex(tmpLink->getFromNode());
		}
	}
	return nullptr;
}

ASTNode* AST::getNode(unsigned int lineNumber)
{
	for (unsigned int i=0; i<this->nodes.size();++i)
	{
		ASTNode* tmpNode = this->nodes.at(i);
		if(tmpNode->getLineNumber()==lineNumber)
		{
			return tmpNode;
		}
	}
	return nullptr;
}

ASTNode* AST::getNodeByIndex(unsigned int nodeIndex)
{
	for (unsigned int i=0; i<this->nodes.size();++i)
	{
		if(this->nodes.at(i)->getNodeIndex()==nodeIndex)
		{
			return this->nodes.at(i);
		}
	}
	// if there are no results return null OR exception can be thrown NODE_NOT_FOUND
	return nullptr;
}

//Query
unsigned int AST::getFollow(unsigned int targetStatement)
{
	ASTNode* result = this->getSiblingNode( getNode(targetStatement) );
	if(result!=nullptr)
	{
		return result->getLineNumber();
	} else {
		return 0;
	}
}
unsigned int AST::getFollowBy(unsigned int targetStatement)
{
	ASTNode* result = this->getRevSiblingNode( getNode(targetStatement) );
	if(result!=nullptr)
	{
		return result->getLineNumber();
	} else {
		return 0;
	}
}
vector<unsigned int> AST::getFollowTC(unsigned int targetStatement)
{
	vector<unsigned int> result;
	unsigned int callResult;
		
	callResult = _getFollowTC(targetStatement);
	while(callResult != 0)
	{
		result.push_back(callResult);
		callResult = _getFollowTC(callResult);
	}

	// if there are no results, vector size will be 0
	return result;
}

unsigned int AST::_getFollowTC(unsigned int targetStatement)
{
	ASTNode* node = getNode(targetStatement);
	for (unsigned int i=0; i<this->siblingTable.size();++i)
	{
		SiblingLink* tmpLink = this->siblingTable.at(i);
		if(tmpLink->getFromNode()==node->getNodeIndex())
		{
			return getNodeByIndex(tmpLink->getToNode())->getLineNumber();
		}
	}
	return 0;
}

vector<unsigned int> AST::getFollowTCBy(unsigned int targetStatement)
{
	//return nullptr;
	vector<unsigned int> result;
	unsigned int callResult;
		
	callResult = _getFollowTCBy(targetStatement);
	while(callResult != 0)
	{
		result.push_back(callResult);
		callResult = _getFollowTCBy(callResult);
	}

	// if there are no results, vector size will be 0
	return result;
}

unsigned int AST::_getFollowTCBy(unsigned int targetStatement)
{
	ASTNode* node = getNode(targetStatement);
	for (unsigned int i=0; i<this->siblingTable.size();++i)
	{
		SiblingLink* tmpLink = this->siblingTable.at(i);
		if(tmpLink->getToNode()==node->getNodeIndex())
		{
			return getNodeByIndex(tmpLink->getFromNode())->getLineNumber();
		}
	}
	return 0;
}

bool AST::isFollow(unsigned int targetStatement_1, unsigned int targetStatement_2)
{
	if(this->getFollow(targetStatement_1)==targetStatement_2)
	{
		return true;
	} else {
		return false;
	}
}
bool AST::isFollowTC(unsigned int targetStatement_1, unsigned int targetStatement_2)
{
	vector<unsigned int> resultset = getFollowTCBy( targetStatement_1 );
	for (unsigned int i=0; i<resultset.size();++i)
	{
		if(resultset.at(i)==targetStatement_2)
		{
			return true;
		}
	}
	return false;
}

unsigned int AST::getParent(unsigned int targetStatement)
{
	if( this->getParentNode(getNode(targetStatement)) != nullptr )
	{
		return this->getParentNode(getNode(targetStatement))->getLineNumber();
	} else {
		return 0;
	}
}

vector<unsigned int> AST::getParentBy(unsigned int targetStatement)
{
	vector<ASTNode*> resultset = this->getRevParentNode(getNode(targetStatement));
	vector<unsigned int> result;
	for (unsigned int i=0; i<resultset.size();++i)
	{
		result.push_back( resultset.at(i)->getLineNumber() );
	}
	return result;
}

vector<unsigned int> AST::getParentTC(unsigned int targetStatement)
{
	ASTNode* node = getNode(targetStatement);

	//return nullptr;
	vector<unsigned int> result;
	ASTNode* callResult;
		
	callResult = getParentNode(node);
	while(callResult != nullptr)
	{
		result.push_back(callResult->getLineNumber());
		callResult = getParentNode(callResult);
	}

	// if there are no results, vector size will be 0
	return result;
}

vector<unsigned int> AST::getParentByTC(unsigned int targetStatement)
{
	vector<unsigned int> result;
	vector<ASTNode*> callResult;
	
	callResult = this->getRevParentNode(getNode(targetStatement));
	if(callResult.size()!=0)
	{
		for (unsigned int i=0; i<callResult.size();++i)
		{
			vector<unsigned int> tmpresult;
			result.push_back( callResult.at(i)->getLineNumber() );
			tmpresult = getParentByTC(callResult.at(i)->getLineNumber());
			for (unsigned int j=0; j<tmpresult.size();++j)
			{
				result.push_back(tmpresult.at(j));
			}
		}
	}
	return result;
}

bool AST::isParent(unsigned int targetStatement_1, unsigned int targetStatement_2)
{
	ASTNode* result = this->getParentNode(getNode(targetStatement_2));
	if(result!=nullptr && result->getLineNumber()==targetStatement_1)
	{
		return true;
	} else {
		return false;
	}
}

bool AST::isParentTC(unsigned int targetStatement_1, unsigned int targetStatement_2)
{
	vector<unsigned int> result = getParentByTC(targetStatement_1);
	if(result.size() > 0)
	{
		for (unsigned int i=0; i<result.size();++i)
		{
			if(result.at(i)==targetStatement_2)
			{
				return true;
			}
		}
	} else {
		return false;
	}
	return false;
}
